package com.uplooking.service.impl;

import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import javax.transaction.Transactional.TxType;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson.JSONObject;
import com.uplooking.constant.RedisConstants;
import com.uplooking.constant.ShopConstants;
import com.uplooking.dao.GoodDO;
import com.uplooking.dao.OrderDO;
import com.uplooking.dao.ShopDO;
import com.uplooking.pojo.DictVO;
import com.uplooking.pojo.GoodVO;
import com.uplooking.pojo.OrderVO;
import com.uplooking.service.ShopService;

@Service("shopService")
public class ShopServiceImpl implements ShopService {

	@Autowired
	private StringRedisTemplate redis;
	
	@Autowired
	private GoodDO goodDO;
	
	@Autowired
	private ShopDO shopDO;
	
	@Autowired
	private OrderDO orderDO;
	
	@Override
	public List<DictVO> getTypeList() {
		String text = redis.opsForHash().get(RedisConstants.PrefixDict, 
				RedisConstants.PrefixDictList+ShopConstants.DictGoodTypes).toString();
		return JSONObject.parseArray(text, DictVO.class);
	}

	@Override
	@Transactional(value=TxType.SUPPORTS)
	public Page<GoodVO> getGoodList(String shop, int page, int size) {
		return goodDO.findAll(new Specification<GoodVO>() {
			@Override
			public Predicate toPredicate(Root<GoodVO> rt, CriteriaQuery<?> cq, CriteriaBuilder cb) {
				return cb.equal(rt.get("gshop").as(String.class), shop);
			}
		}, PageRequest.of(page, size));
	}

	@Override
	@Transactional(value=TxType.REQUIRED)
	public Map<String, Object> addGood(GoodVO goodVO, MultipartFile picture) throws Exception {
		String gno = UUID.randomUUID().toString();
		goodVO.setGno(gno);
		goodVO.setGstatus(ShopConstants.GoodDownStatus);
		//更新添加数据
		goodDO.saveAndFlush(goodVO);
		//图片
		redis.opsForHash().put(RedisConstants.PrefixImages, 
				RedisConstants.PrefixImagesPicture+gno, 
				Base64Utils.encodeToString(picture.getBytes()));
		//缓存 每个商品都要有缓存 商品信息 保证信息完整性(外键关系)
		goodVO.setShopVO(shopDO.findById(goodVO.getGshop()).orElse(null));
		goodVO.setDictVO(JSONObject.parseObject(
				redis.opsForHash().get(RedisConstants.PrefixDict, 
						RedisConstants.PrefixDictValue+goodVO.getGstatus()).toString(), 
				DictVO.class));
		redis.opsForValue().set(RedisConstants.PrefixGood+goodVO.getGno(), 
				JSONObject.toJSONString(goodVO));
		return ShopConstants.AddGoodSuccess();
	}

	@Override
	@Transactional(value=TxType.REQUIRED)
	public Map<String, Object> updownGood(String no) throws Exception {
		//先改缓存 （分布式锁） 持久化
		String lock = RedisConstants.PrefixLock+no;
		String value = UUID.randomUUID().toString();
		//加锁 设置过期时间
		try {
			if(redis.opsForValue().setIfAbsent(lock, value, 
					RedisConstants.LockExpire, TimeUnit.SECONDS)) {
				String key = RedisConstants.PrefixGood+no;
				if(redis.hasKey(key)) {
					String text = redis.opsForValue().get(key);
					GoodVO goodVO = JSONObject.parseObject(text, GoodVO.class);
					if(ShopConstants.GoodUpStatus == goodVO.getGstatus()) {
						//下架 
						goodVO.setGstatus(ShopConstants.GoodDownStatus);
					}else if(ShopConstants.GoodDownStatus == goodVO.getGstatus()) {
						//上架
						goodVO.setGstatus(ShopConstants.GoodUpStatus);
					}
					//注意修改外键关系字典表数据
					goodVO.setDictVO(JSONObject.parseObject(
							redis.opsForHash().get(RedisConstants.PrefixDict, 
									RedisConstants.PrefixDictValue+goodVO.getGstatus()).toString(), 
							DictVO.class));
					//缓存修改
					redis.opsForValue().set(key, JSONObject.toJSONString(goodVO));
					//数据修改
					goodDO.save(goodVO);
					return ShopConstants.UpDownGoodSuccess();
				}else {
					return ShopConstants.GoodNotExists();
				}
			}else {
				return ShopConstants.LockGoodError();
			}
		} finally {
			//释放锁
			if(value.equals(redis.opsForValue().get(lock))) {
				redis.delete(lock);
			}
		}
	}

	@Override
	@Transactional(value=TxType.SUPPORTS)
	public Page<OrderVO> getOrderList(String shop, int page, int size) {
		return orderDO.findAll(new Specification<OrderVO>() {
			@Override
			public Predicate toPredicate(Root<OrderVO> rt, CriteriaQuery<?> cq, CriteriaBuilder cb) {
				return cb.equal(rt.get("user").as(String.class), shop);
			}
		}, PageRequest.of(page, size));
	}
}
